热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

提升视觉效果:Unity3D中的HDR与Bloom技术(高动态范围成像与光线散射)

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Unity3d HDR和Bloom效果(高动态范围图像和泛光)相关的知识,希望对你有一定的参考价值。 文章开始先放两组效果,文章结尾再放

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Unity3d HDR和Bloom效果(高动态范围图像和泛光)相关的知识,希望对你有一定的参考价值。



文章开始先放两组效果,文章结尾再放两组效果
本文测试场景资源来自浅墨大神,shader效果为本文效果




HDR
人们有限的视觉系统,只支持16.7百万的颜色,超出这个范围的颜色就不能显示了
bmp或jprg每个像素就是16,24或32位
每个像素都由红绿蓝构成,如果储存为24位,每个值的范围就在0,255之间,
只能表现出256:1的差别,unity的shader中是0到1
然而在自然中太阳光下的对比度是50000:1
HDR(High Dynamic Range)使图像能表现出更大范围的对比,普通的范围就叫LDR(Low Dynamic Range)

你在照相的时候能控制曝光时间从而控制亮度。
HDR效果就是可控的曝光,


色调映射 tone-mapping


传统的显示设备不能完全的显示出HDR,所以我们用tone-mapping技术。
tone-mapping让图像从HDR映射为LDR显示
http://www.ownself.org/blog/2011/tone-mapping.html中的解释很好
Tone Mapping原是摄影学中的一个术语,因为打印相片所能表现的亮度范围不足以表现现实世界中的亮度域,而如果简单的将真实世界的整个亮度域线性压缩到照片所能表现的亮度域内,则会在明暗两端同时丢失很多细节,这显然不是所希望的效果,Tone Mapping就是为了克服这一情况而存在的,既然相片所能呈现的亮度域有限则我们可以根据所拍摄场景内的整体亮度通过光圈与曝光时间的长短来控制一个合适的亮度域,这样既保证细节不丢失,也可以不使照片失真。人的眼睛也是相同的原理,这就是为什么当我们从一个明亮的环境突然到一个黑暗的环境时,可以从什么都看不见到慢慢可以适应周围的亮度,所不同的是人眼是通过瞳孔来调节亮度域的。
一个tone-mapping的公式

 




Middlegrey为全屏幕或部分屏幕的中间灰度,可以控值屏幕的亮度
AvgLogLuminance就是全屏幕或部分屏幕的亮度的对数的平均值
 
AvgLogLuminance的公式
Lw是亮度,n是所取亮度数

这个操作能让L值限制位[0,1)
一些tone-mapping操作用exposure或gamma作为参数控制最终的图像。
tone-mapping是非线性的,他对暗色保有一定范围并且对亮色逐步接近动态
这个技术产生吸引人的视觉效果,有着强烈的对比和细节。

HDR Rendering In OpenGL一文中给出简要且效果不错的公式
 
关键代码如下

float4 frag(v2f i) :COLOR

float4 c = tex2D(_MainTex, i.uv_MainTex);
float y = dot(float4(0.3,0.59,0.11,1),c);
float yd = _Exp * (_Exp / _BM + 1) / (_Exp + 1);
return c*yd;


_Exp,_BM为外部可控变量



HDR流程如下

 




如果分不清HDR与加亮light,可以看看skybox,加亮light是不会加亮skybox的,HDR使颜色更鲜明,像素更清晰。



Bloom泛光
辉光的原因是由于人眼晶状体的散射
我们制造bloom的原理是把图像的亮的部分通过卷积模糊再叠加到原图像上,就产生了bloom效果。
高斯模糊的滤波器是一种低通滤波器
就是去当前像素和周围的像素按一定权重混合,产生一定模糊效果

权重分布如下,离当前像素越远,权重越低



 
高斯正态分布曲线

 



二维公式

可以通过这个公式直接算出权重

double sigma = (double)radius / 3.0;
double sigma2 = 2.0 * sigma * sigma;
double sigmap = sigma2 * PI;
for(long n &#61; 0, i &#61; - radius; i <&#61;radius; &#43;&#43;i)

long i2 &#61; i * i;
for(long j &#61; -radius; j <&#61; radius; &#43;&#43;j, &#43;&#43;n)
kernel[n] &#61; exp(-(double)(i2 &#43; j * j) / sigma2) / sigmap;




Kernel即为权重

Radius为所求像素与当前像素距离&#xff08;半径&#xff09;





针对这个公式我们可以算出3*3&#xff0c;5*5,7*7等滤波器&#xff0c;出于性能考虑&#xff0c;我们还是使用5*5滤波器


 3*3滤波器



 5*5滤波器

有现成的就不算了&#xff0c;算这个也消耗一些性能
我们直接用这个权重
关键代码如下

float3 mc00 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(2,2)/_inten).rgb;
float3 mc10 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(1,2)/_inten).rgb;
float3 mc20 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(0,2)/_inten).rgb;
float3 mc30 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(-1,2)/_inten).rgb;
float3 mc40 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(-2,2)/_inten).rgb;
float3 mc01 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(2,1)/_inten).rgb;
float3 mc11 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(1,1)/_inten).rgb;
float3 mc21 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(0,1)/_inten).rgb;
float3 mc31 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(-1,1)/_inten).rgb;
float3 mc41 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(-2,1)/_inten).rgb;
float3 mc02 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(2,0)/_inten).rgb;
float3 mc12 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(1,0)/_inten).rgb;
float3 mc22mc &#61; tex2D (_MainTex, i.uv_MainTex).rgb;
float3 mc32 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(-1,0)/_inten).rgb;
float3 mc42 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(-2,0)/_inten).rgb;

float3 mc03 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(2,-1)/_inten).rgb;
float3 mc13 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(1,-1)/_inten).rgb;
float3 mc23 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(0,-1)/_inten).rgb;
float3 mc33 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(-1,-1)/_inten).rgb;
float3 mc43 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(-2,-1)/_inten).rgb;
float3 mc04 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(2,-2)/_inten).rgb;
float3 mc14 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(1,-2)/_inten).rgb;
float3 mc24 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(0,-2)/_inten).rgb;
float3 mc34 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(-1,-2)/_inten).rgb;
float3 mc44 &#61; tex2D (_MainTex, i.uv_MainTex-fixed2(-2,-2)/_inten).rgb;
float3 c&#61;0;
c&#43;&#61;(mc00&#43;mc40&#43;mc04&#43;mc44);//4
c&#43;&#61;4*(mc10&#43;mc30&#43;mc14&#43;mc34&#43;mc01&#43;mc41&#43;mc03&#43;mc43);//16
c&#43;&#61;7*(mc20&#43;mc24&#43;mc02&#43;mc42);//16
c&#43;&#61;16*(mc11&#43;mc13&#43;mc03&#43;mc33);//32
c&#43;&#61;26*(mc21&#43;mc23&#43;mc12&#43;mc32);//64
c&#43;&#61;41*mc22mc;//32
c/&#61;273;


_inten为模糊程度





觉得冗长麻烦也可用for循环代替。





然后我们要取其中的亮色部分与原有图像混合&#xff0c;

这一部分直接调用unity内部函数Luminance函数求出亮度&#xff0c;把它与模糊的图像相乘&#xff0c;暗色部分自然消除

但如果直接相乘就会在暗色的边缘产生不自然的黑影&#xff0c;就是把暗色也“泛光了”&#xff0c;为此我们不让Luminance后的值为0&#xff0c;再加上0.1&#xff0c;也不影响亮度。

float lum &#61; Luminance(c);
c &#61; mc22mc &#43; c * (lum&#43;0.1) * _Lum;
return float4(c,1);






最终与HDR结合再一起就是上图例子的最终效果







最后一道工序就是放入相机中&#xff0c;我们建立一个c#并负责传值

代码如下&#xff1a;


using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class HDRGlow : MonoBehaviour
#region Variables
public Shader curShader;
private Material curMaterial;
public float exp &#61; 0.4f;
public float bm &#61; 0.4f;
public int inten &#61; 512;
public float lum &#61; 1f;
#endregion
#region Properties
Material material

get

if (curMaterial &#61;&#61; null)

curMaterial &#61; new Material(curShader);
curMaterial.hideFlags &#61; HideFlags.HideAndDontSave;

return curMaterial;


#endregion
void Start()

if (!SystemInfo.supportsImageEffects)

enabled &#61; false;
return;

if (!curShader && !curShader.isSupported)

enabled &#61; false;


void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)

if (curShader !&#61; null)

material.SetFloat("_Exp", exp);
material.SetFloat("_BM", bm);
material.SetFloat("_Inten", inten);
material.SetFloat("_Lum", lum);
Graphics.Blit(sourceTexture, destTexture, material);

else

Graphics.Blit(sourceTexture, destTexture);


void OnDisable()

if (curMaterial)

DestroyImmediate(curMaterial);















Unity 的imageEffect有一个叫做BloomAndLensFlares

与本文的差别是多了色彩平衡和lens flare效果&#xff0c;可以试着再加上去


                                                        -------  by wolf96 http://blog.csdn.net/wolf96




推荐阅读
  • 本文探讨了如何利用HTML5和JavaScript在浏览器中进行本地文件的读取和写入操作,并介绍了获取本地文件路径的方法。HTML5提供了一系列API,使得这些操作变得更加简便和安全。 ... [详细]
  • 深入解析Java枚举及其高级特性
    本文详细介绍了Java枚举的概念、语法、使用规则和应用场景,并探讨了其在实际编程中的高级应用。所有相关内容已收录于GitHub仓库[JavaLearningmanual](https://github.com/Ziphtracks/JavaLearningmanual),欢迎Star并持续关注。 ... [详细]
  • 目录一、salt-job管理#job存放数据目录#缓存时间设置#Others二、returns模块配置job数据入库#配置returns返回值信息#mysql安全设置#创建模块相关 ... [详细]
  • 实用正则表达式有哪些
    小编给大家分享一下实用正则表达式有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下 ... [详细]
  • 本文详细介绍了Java库XChart中的XYSeries类下的setLineColor()方法,并提供了多个实际应用场景的代码示例。 ... [详细]
  • 在项目部署后,Node.js 进程可能会遇到不可预见的错误并崩溃。为了及时通知开发人员进行问题排查,我们可以利用 nodemailer 插件来发送邮件提醒。本文将详细介绍如何配置和使用 nodemailer 实现这一功能。 ... [详细]
  • 本文详细解析了Java中hashCode()和equals()方法的实现原理及其在哈希表结构中的应用,探讨了两者之间的关系及其实现时需要注意的问题。 ... [详细]
  • 雨林木风 GHOST XP SP3 经典珍藏版 V2017.11
    雨林木风 GHOST XP SP3 经典珍藏版 V2017.11 ... [详细]
  • 丽江客栈选择问题
    本文介绍了一道经典的算法题,题目涉及在丽江河边的n家特色客栈中选择住宿方案。两位游客希望住在色调相同的两家客栈,并在晚上选择一家最低消费不超过p元的咖啡店小聚。我们将详细探讨如何计算满足条件的住宿方案总数。 ... [详细]
  • 2018-2019学年第六周《Java数据结构与算法》学习总结
    本文总结了2018-2019学年第六周在《Java数据结构与算法》课程中的学习内容,重点介绍了非线性数据结构——树的相关知识及其应用。 ... [详细]
  • Nginx 反向代理与负载均衡实验
    本实验旨在通过配置 Nginx 实现反向代理和负载均衡,确保从北京本地代理服务器访问上海的 Web 服务器时,能够依次显示红、黄、绿三种颜色页面以验证负载均衡效果。 ... [详细]
  • 本文介绍了SVD(奇异值分解)和QR分解的基本原理及其在Python中的实现方法。通过具体代码示例,展示了如何使用这两种矩阵分解技术处理图像数据和计算特征值。 ... [详细]
  • 本文深入探讨了SQL数据库中常见的面试问题,包括如何获取自增字段的当前值、防止SQL注入的方法、游标的作用与使用、索引的形式及其优缺点,以及事务和存储过程的概念。通过详细的解答和示例,帮助读者更好地理解和应对这些技术问题。 ... [详细]
  • 本题来自WC2014,题目编号为BZOJ3435、洛谷P3920和UOJ55。该问题描述了一棵不断生长的带权树及其节点上小精灵之间的友谊关系,要求实时计算每次新增节点后树上所有可能的朋友对数。 ... [详细]
  • 由中科院自动化所、中科院大学及南昌大学联合研究提出了一种新颖的双路径生成对抗网络(TP-GAN),该技术能通过单一侧面照片生成逼真的正面人脸图像,显著提升了不同姿态下的人脸识别效果。 ... [详细]
author-avatar
ll66068ll你
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有